/* 算法实现模板 */
import AlgorithmBase from "./AlgorithmBase";
import { txtToMatrix, mdRender} from "../../../common/Common";

import { showMathStringInTable } from "../../../visual/Table2D";
import { drawDecisionTree } from "../../../visual/Tree2D";
import { DeNode } from "../../../common/DecisionTreeFuncs";
import TDom from "../../../common/TDom";
import TCommon from "../../../common/TCommon";

import 'katex/dist/katex.min.css';

class TreePruning extends AlgorithmBase{
  constructor(){
    super('TreePruning');

    this.model          = {dataSet:[ ],label:[ ],alpha:10.0}

    this.descriptionUrl = `${this.homepage}/data/slm/ch5/5.4/README.md`;
    this.datasetUrl     = `${this.homepage}/data/slm/ch5/5.4/dataset.txt`;

    this.trainLog = [];
    this.len = 0
    this.Ct = 0 
    this.max = 0
    this.flag = 0
    this.node_name_ = ''
    this.cmd =''
    this.width = 0
  }

  init(){ 
    this.reset();

    this.initTrainDS();
  }
  
  reset(){
    this.dataset = undefined;
    this.trainDS = undefined;
    this.model          = {dataSet:[ ],label:[ ],alpha:10.0}

    this.clear();

    this.initDescription();
    this.updateModel();
  }

  loadModel(json){
    if(json.TrainDS){
      if(json.TrainDS instanceof Array){
        this.trainDSList = [];
        for(let i = 0, il = json.TrainDS.length; i < il; i++){
          const dat = json.TrainDS[i];
          this.trainDSList.push(dat);
        }
        this.trainDS = this.trainDSList[0];
      }
      else{
        let trainDS = json.TrainDS;
        this.trainDS = trainDS;
      }
    }
    return this.trainDS
  }
  
  // 模型UI
  initModelUI(){
    this.addLabel(this.domElementList.model, '参数a：');
    this.alphaInput = this.addFloatInput(
      this.domElementList.model, this.model.alpha, 
      (event)=>{
        const val = parseFloat(event.target.value);
        if(val<0){
          this.model.alpha = 0
          event.target.value = 0
        }
        else this.model.alpha = val;
        
        this.updateModel();
      });

    this.modelDiv = this.addDiv(this.domElementList.model);
  }

  // 渲染模型到页面
  updateModel(data, label, tree){

    // 显示数据集
    if(this.trainDS){
      if(data === undefined) data = this.trainDS.dataSet;
      if(label === undefined) label = this.trainDS.label;
      if(tree === undefined) tree = this.trainDS.tree;

      // console.log(this.trainDS);

      const modelString = String.raw
      `$$ \begin{aligned} \alpha&:=${this.model.alpha}\end{aligned} $$`

      var table_data = [];

      var data_label_ = ["ID"];
      for(var i=0;i<label.length;i++){
        data_label_.push("\\textbf{"+label[i]+"}")
      }

      table_data.push(data_label_);

      for(var j=0;j<data.length;j++){
        var data_=[]
        data_.push((j+1).toString())
        for(var k=0;k<data[j].length;k++){
          data_.push("\\textbf{"+data[j][k]+"}")
        }
        table_data.push(data_)
      }

      console.log(data);
      if(this.inputDiv) this.inputDiv.innerText = '';
      // mdRender(`${modelString}`, this.inputDiv);
      showMathStringInTable(this.inputDiv,table_data);
      drawDecisionTree(this.inputDiv,tree);

    }
  }

  // 输入数据UI
  initInputUI(){

    // 文件选择器
    const fileSelector      = document.createElement('input');
    fileSelector.type       = 'file';
    fileSelector.hidden     = true;
    fileSelector.onchange   = this.handleFileChange.bind(this);   
    
    // this.addButton(this.domElementList.input, "获取服务器数据", this.initTrainDS.bind(this));
    TDom.createButton(this.domElementList.input, "获取服务器数据", this.initTrainDS.bind(this));
    this.addButton(this.domElementList.input, "使用本地数据", this.openFileSelector.bind(this));
    this.domElementList.input.appendChild(fileSelector);
    this.addButton(this.domElementList.input, "训练", this.train.bind(this));
    // this.playButton = this.addButton(this.domElementList.input, "训练动画", this.trainAnimation.bind(this));
    // this.playButton.hidden = true;

    this.labelTrainTips = this.addLabel(this.domElementList.input, '');
    this.inputDiv       = this.addDiv(this.domElementList.input);

    this.fileSelector  = fileSelector;
  }

  // 输出UI
  initOutputUI(){
    this.outputDiv   = this.addDiv(this.domElementList.output);
    this.trainLogDiv = this.addDiv(this.domElementList.output);
  }

  // 获取描述md文件
  initDescription(){
    TDom.getDataFromUrl(this.descriptionUrl, (txt)=>{
      mdRender(txt, this.descriptionDiv);
    })
    // fetch(this.descriptionUrl).then((response)=>{
    //   return response.text();
    // }).then((txt)=>{

    //   this.reandMD(txt, this.descriptionDiv)
    // })
  }

  // 获取服务器数据集
  initTrainDS(){
    this.prepare(this.datasetUrl);
  }

  // 准备数据训练数据
  prepare(url){
    fetch(url).then((response)=>{
      return response.json();
    }).then((json)=>{
      this.trainDS = this.loadModel(json);
      console.log(this.trainDS);
      this.updateModel();
    })

    // TDom.getDataFromUrl(url, 
    //   (txt)=>{
    //   this.dataset = txtToMatrix(txt);
    //   console.log(this.dataset);
    //   this.trainDS = TCommon.loadDataset(this.dataset);
    //   this.updateModel();
    // })
  }

  openFileSelector(){
    this.fileSelector.click();
  }
  // 获取文件路径加载
  handleFileChange(event){
    const files = event.target.files;

    for(let i = 0, il = files.length; i < il; i++){
      let reader = new FileReader();
      reader.onload = (e) => {      
        this.prepare(e.target.result);
        if(i === files.length - 1){
          event.target.value = '';
        }
      };
      reader.readAsDataURL(files[i])
    }
  }

  // 训练
  train(){
    const dataSet = this.trainDS.dataSet
    const label = this.trainDS.label

    var tree = new DeNode(this.trainDS.tree)

    var trainLog = [];
    var count = 0 
    trainLog.push(["\\text{迭代次数}","待剪枝节点","\\text{剪枝前}C_a (T_b)","\\text{剪枝后}C_a (T_a)","\\text{是否剪枝}"])

    while(tree.getDepth()>1){
      var Cta = tree.cal_cost(dataSet, label,this.model.alpha)
      var flag = 0
      for(var i=0;i<tree.getWidth();i++){
        var new_ = new DeNode(tree.purn(dataSet,label,-i))
        if(JSON.stringify(tree.node) === JSON.stringify(new_.node))
        {
          continue
        }
        var Ctb = new_.cal_cost(dataSet,label,this.model.alpha)
        count++
        if(Ctb <= Cta){
          trainLog.push([`${count}`, `${tree.node_name}`, `${Cta}`, `${Ctb}`, "\\text{是}"])
          tree = new DeNode(Object.assign({}, new_.node));
          flag = 1
          break
        }
        else{
          trainLog.push([`${count}`, `${tree.node_name}`, `${Cta}`, `${Ctb}`, "\\text{否}"])
        }
      }
      if(!flag){
        break
      }
    }
    if(this.trainLogDiv) this.trainLogDiv.innerHTML = '';
    drawDecisionTree(this.trainLogDiv, tree.node);
    showMathStringInTable(this.trainLogDiv, trainLog);

  }
}


export default TreePruning;